package hn.cch.apache.http;

import org.apache.commons.io.FileUtils;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class HttpClientUtil {

    private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

    public static CloseableHttpResponse send(HttpRequestBase httpRequest) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            RequestConfig requestConfig = RequestConfig.custom()
                // setConnectTimeout    建立连接超时时间
                // setSocketTimeout     数据交互超时时间
                .setConnectTimeout(60 * 1000).setSocketTimeout(60 * 1000)
                // setConnectionRequestTimeout  从连接池获取连接的超时时间
                .setConnectionRequestTimeout(60 * 1000).build();
            httpRequest.setConfig(requestConfig);
            CloseableHttpResponse httpResponse = httpClient.execute(httpRequest);
            return httpResponse;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public static String sentGet(String url) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpGet httpGet = new HttpGet(url);
            CloseableHttpResponse httpResponse = send(httpGet);
            String content = EntityUtils.toString(httpResponse.getEntity());
            return content;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public static String sentPostJson(String url, String str) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader(HTTP.CONTENT_TYPE, ContentType.APPLICATION_JSON.getMimeType());
            httpPost.setEntity(new StringEntity(str));

            CloseableHttpResponse httpResponse = send(httpPost);

            String content = EntityUtils.toString(httpResponse.getEntity());
            return content;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static byte[] sentPostUrlEncodedForm(String url, Map<String, String> map) {

        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader(HTTP.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
            List<NameValuePair> nameValuePairs = new ArrayList<>();
            map.forEach((key, value) -> {
                nameValuePairs.add(new BasicNameValuePair(key, value));
            });
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
            byte[] buffer = EntityUtils.toByteArray(httpResponse.getEntity());

            return buffer;
        } catch (IOException e) {
            e.printStackTrace();
            return new byte[0];
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static byte[] sentPost(String url, byte[] buf) {

        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new ByteArrayEntity(buf));

            CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
            byte[] buffer = EntityUtils.toByteArray(httpResponse.getEntity());

            return buffer;
        } catch (IOException e) {
            e.printStackTrace();
            return new byte[0];
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public static File saveFile(CloseableHttpResponse httpResponse, String downloadPath) throws IOException {

        // 如果文件名称没有就使用UUID
        String filename = UUID.randomUUID().toString();
        Header firstHeader = httpResponse.getFirstHeader("Content-Disposition");
        HeaderElement[] headerElements = firstHeader.getElements();
        if (headerElements.length > 0) {
            NameValuePair nameValuePair = headerElements[0].getParameterByName("filename");
            if (nameValuePair != null) {
                // 解码下载文件名称
                filename = URLDecoder.decode(nameValuePair.getValue(), Charset.defaultCharset().displayName());
            }
        }

        File file = new File(downloadPath + filename);
        if (file.exists()) {// 如果存在就先删除
            file.delete();
        }
        logger.info("file path:{}", file.getPath());
        byte[] buffer = EntityUtils.toByteArray(httpResponse.getEntity());// 读取返回数据
        FileUtils.writeByteArrayToFile(file, buffer);// 写入文件数据
        return file;

    }


    /**
     * 下载文件
     *
     * @param url
     * @param downloadPath
     * @return
     */
    public static File downloadGet(String url, String downloadPath) {
        try {
            HttpGet httpGet = new HttpGet(url);
            CloseableHttpResponse httpResponse = send(httpGet);
            StatusLine statusLine = httpResponse.getStatusLine();
            int statusCode = statusLine.getStatusCode();
            if (statusCode == HttpStatus.SC_OK) { // 返回成功
                ContentType contentType = ContentType.get(httpResponse.getEntity());
                // long contentLength = entity.getContentLength();

                if (ContentType.APPLICATION_OCTET_STREAM.equals(contentType)) {
                    File file = saveFile(httpResponse, downloadPath);
                    return file;
                }
            } else {
                String reasonPhrase = statusLine.getReasonPhrase();
                logger.error("statusCode:{},reasonPhrase:{}", statusCode, reasonPhrase);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static File downloadPost() {
        CloseableHttpClient httpClient = HttpClients.createDefault();

        return null;
    }


    /**
     * 按照form发送post请求
     *
     * @param url
     * @param map
     * @return
     */
    public static String sentForm(String url, Map<String, String> map) {


        try {
            MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
            ContentType contentType = ContentType.create(ContentType.MULTIPART_FORM_DATA.getMimeType());

            map.forEach((name, text) -> {
                multipartEntityBuilder.addTextBody(name, text, contentType);
            });

            // multipartEntityBuilder.addTextBody("empName", "张振", contentType);
            // multipartEntityBuilder.addTextBody("empGender", "男", contentType);
            // multipartEntityBuilder.addTextBody("empDept", "开发部", contentType);
            // multipartEntityBuilder.addTextBody("empEmail", "hangcchhn@163.com", contentType);
            // multipartEntityBuilder.addTextBody("empPhone", "17362505924", contentType);

            HttpEntity httpEntity = multipartEntityBuilder.build();

            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(httpEntity);

            CloseableHttpResponse httpResponse = send(httpPost);

            String content = EntityUtils.toString(httpResponse.getEntity());
            return content;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 将类对象转化为键值对:若属性为空则不转
     *
     * @param prefix 属性拼接前缀
     * @param object 对象
     * @param starts 包前缀
     * @return
     */
    public static Map<String, Object> setForm(String prefix, Object object, String starts) {
        Map<String, Object> map = new HashMap<>();
        try {
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                String fieldName = field.getName();
                String className = field.getType().getName();
                field.setAccessible(true);
                Object access = field.get(object);
                if (access != null) {
                    if (className.startsWith(starts)) {
                        map.putAll(setForm(prefix + fieldName + ".", access, starts));
                    } else {
                        map.put(prefix + fieldName, access);
                    }
                }


            }

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return map;
    }


    public static byte[] sentHttps(String http, byte[] data, String keystore, String password) {

        CloseableHttpClient httpClient = null;

        try {

            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            InputStream inputStream = new FileInputStream(new File(keystore));
            trustStore.load(inputStream, password.toCharArray());
            TrustStrategy trustStrategy = new TrustSelfSignedStrategy();
            SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore, trustStrategy).build();


            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", new SSLConnectionSocketFactory(sslContext))
                .build();

            httpClient = HttpClients.custom().setConnectionManager(
                new PoolingHttpClientConnectionManager(socketFactoryRegistry)).build();


            // SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new File(keystore),
            //         password.toCharArray(), new TrustSelfSignedStrategy()).build();
            // httpClient = HttpClients.custom().
            //         setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext)).build();

            HttpPost httpPost = new HttpPost(http);
            httpPost.setEntity(new ByteArrayEntity(data));


            CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
            byte[] buffer = EntityUtils.toByteArray(httpResponse.getEntity());

            return buffer;
        } catch (Exception e) {
            e.printStackTrace();
            return new byte[0];
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public static String sslHttps(String http, String data, String keystore, String password) {

        CloseableHttpClient httpClient = null;
        try {

            SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            InputStream inputStream = new FileInputStream(new File(keystore));
            trustStore.load(inputStream, password.toCharArray());
            sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    return true;
                }
            });
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build(), new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslSession) {
                    return true;
                }
            });
            httpClient = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();

            HttpPost httpPost = new HttpPost(http);
            httpPost.addHeader(HTTP.CONTENT_TYPE, ContentType.APPLICATION_JSON.getMimeType());

            httpPost.setEntity(new StringEntity(data));

            CloseableHttpResponse httpResponse = httpClient.execute(httpPost);

            String content = EntityUtils.toString(httpResponse.getEntity());
            return content;
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        } finally {
            try {
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
